/* OpenTeddy 3d sketch modeler based on the paper by T. Igarashi et al.
 *      "Teddy: A Sketching Interface for 3D Freeform Design"
 *
 * Copyright (c) 2004 Alex Ni
 *
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */


#ifndef OT_INFLATE_H
#define OT_INFLATE_H

#include "otconfig.h"
#pragma warning(disable:4786)
#include <map>

// explict edge (structure with two point indices)
struct exp_edge {
	int p1;
	int p2;

	exp_edge(){p1=p2=-1;}
	exp_edge(int p1, int p2){ exp_edge::p1 = p1; exp_edge::p2 = p2; }
	bool operator==(const exp_edge& ee) const{
		return ( (p1==ee.p1) && (p2==ee.p2) ) || ( (p2==ee.p1) && (p1==ee.p2) );
	}
	int& operator[](int i) {
		OT_ASSERT((i<=1) && (i>=0) );
		if(i==0) return p1;
		if(i==1) return p2;
		return (int&)*((int*)0);  // hopefully will cause a crash to catch this error
	}
	int operator[](int i) const {
		OT_ASSERT((i<=1) && (i>=0) );
		if(i==0) return p1;
		if(i==1) return p2;
		return (int&)*((int*)0);  // hopefully will cause a crash to catch this error
	}

	bool operator<(const exp_edge& ee1) const
	{
		if (p1 < ee1.p1) 
			return true;

		if (p1 == ee1.p1 && p2 < ee1.p2)
			return true;

		return false;
	}

};

// explict triangle (structure with three point indices)
struct exp_tri {
	int p1;
	int p2;
	int p3;

	exp_tri(){p1=p2=p3=-1;}
	exp_tri(int p1, int p2, int p3){ exp_tri::p1 = p1; exp_tri::p2 = p2; exp_tri::p3 = p3;}

	int& operator[](int i){
		OT_ASSERT((i<=2) && (i>=0) );
		if(i==0) return p1;
		if(i==1) return p2;
		if(i==2) return p3;
		return (int&)*((int*)0);  // hopefully will cause a crash
	}
};

struct gst_return {
	list<int>* fanpts;   // the fan list
	int newp;  // 'ee' replacement center point
	gst_return(){fanpts=NULL; newp=-1; }
};

// the main class.
class ot_inflate {
public:
	ot_inflate(){
		// flags asserting each step is done
		triangulated = false;
		spined = false;
		made_3d = false; 

		// the original 2d points (x0,y0,x1,y1,...
		points = NULL;
		ptct = 0;
		
		// the new points including the original generated by spine phase (x0,y0,x1,y1,...
		npoints = NULL;
		nptct = 0;
		// array of boolean marking if a triangle is visited or not
		// (in searching for root phase)
		fjmarked = NULL;

		// list of triangles generated by spine phase
//		list<exp_tri> newtri;

		// list of interior->exterior edges generated by spine phase
		// and a hash lookup (exp_edge -> index into intext_v)
//		stdext::hash_map<exp_edge, size_t, exp_edge_cmp > intext_h;
//		vector<exp_edge> intext_v;

		// total distance from spine point to exterior point for
		// each spine point
		spine_sum_dist=NULL;
		spine_sum_total=NULL;

		// 3d points generated by 3d shape phase (x0,y0,z0,x1,y1,z1,...
		n3ptct=0;
		n3pts=NULL;

		// list of triangles generated by 3d shape phase
		//list<exp_tri> n3tri;

	}
	~ot_inflate(){
		if(points) delete[] points;
		if(npoints) delete[] npoints;
		if(fjmarked) delete[] fjmarked;
		if(spine_sum_dist) delete[] spine_sum_dist;
		if(spine_sum_total) delete[] spine_sum_total;
		if(n3pts) delete[] n3pts;

		if(out.trianglelist) free(out.trianglelist);
		if(out.neighborlist) free(out.neighborlist);
		if(out.segmentlist) free(out.segmentlist);
		if(out.segmentmarkerlist) free(out.segmentmarkerlist);
		if(out.edgelist) free(out.edgelist);
		if(out.edgemarkerlist) free(out.edgemarkerlist);
	}

	// See comments in constructor to see what these mean.
	bool triangulated;
	bool spined;
	bool made_3d;

// triangulating phase
	double *points;
	size_t ptct;

	struct triangulateio out;

// spine phase
	double* npoints;
	int nptct;

	int* fjmarked;
	list<exp_tri> newtri;

	std::map<exp_edge, size_t > intext_h;

	vector<exp_edge> intext_v;

	double* spine_sum_dist;
	int* spine_sum_total;

// 3d shape phase
	int n3ptct;
	double* n3pts;
	list<exp_tri> n3tri;


private:
	int add_npoint(double p1, double p2, bool external=false);
	bool shedge(int t1, int t2, exp_edge* ee );
	bool adj_sleeve_edge_tri(int tri, exp_edge* ee, exp_edge* adjee, int* adjt);
	int find_root_joint(int tri);
	void add_edge_to_fanpts(list<int>* fanpts, exp_edge ee);
	bool is_external(int pt);
	void add_new_intext_edge(exp_edge ee);
	double newtri_area(exp_tri etri);
	void add_newtri(exp_tri etri);
	void build_tri_fin(list<int>* fanpts, int cenpt);
	gst_return ot_inflate::gen_spine_tris(int tri, exp_edge ee, int force_neigh=0);

public:
	void do_triangulate(double* i_points, size_t i_ptct);
	void generate_spine();
	void generate_3d_shape(int EDGETESS);
};

#endif //OT_INFLATE_H
